package com.test37_反射应用.util.bean;

import com.test37_反射应用.util.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class BeanUtil {
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final ZoneId ZONE_ID = ZoneId.systemDefault();

    private BeanUtil() {    // 构造方法私有化
    }

    /**
     * 实现对象反射属性赋值的处理操作
     * @param object 实例化处理的对象实例(不允许为空)
     * @param value 满足指定格式要求的数据字符串(属性名称|属性内容...)
     */
    public static void setObjectValue(Object object, String value) throws Exception {
        String[] all = IFieldContentSplit.splitAll(value);  // 获取全部的数据信息
        for (String content : all) {
            try {   // 此处唯一可能出现错误的情况就是属性内容不存在
                String[] fields = IFieldContentSplit.splitField(content);
                if (fields[0].contains(".")) {  // 级联关系
                    String[] cascade = fields[0].split("\\.");  // 获取级联的信息
                    Object cascadeInstance = instanceCascadeObject(object, cascade);
                    setFieldValue(cascadeInstance, cascade[cascade.length-1], fields[1]);
                } else {    // 单级属性内容
                    setFieldValue(object, fields[0], fields[1]);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 实现指定对象实例属性内容的设置
     * @param object 当前进行设置的实例化对象
     * @param fieldName 属性名称
     * @param fieldValue 属性设置的内容
     */
    private static void setFieldValue(Object object, String fieldName, String fieldValue) throws Exception {
        Field field = object.getClass().getDeclaredField(fieldName);    // 找到属性
        // 找到属性对应的setter方法
        Method method = object.getClass().getMethod("set" + StringUtil.initcap(fieldName), field.getType());
        method.invoke(object, convert(fieldValue, field));  // 通过反射实现setter方法的调用
    }

    /**
     * 负责对象的级联实例化操作
     * @param object 当前操作的发起对象实例
     * @param cascade 级联的配置关系数组
     * @return 级联对象
     */
    private static Object instanceCascadeObject(Object object, String[] cascade) throws Exception {
        for (int x = 0; x < cascade.length - 1; x++) {
            Method getMethod = object.getClass().getMethod("get" + StringUtil.initcap(cascade[x]));
            Object instance = getMethod.invoke(object);
            if (instance == null) {     // 当前对象还没有实例化
                Field field = object.getClass().getDeclaredField(cascade[x]);
                Object obj = field.getType().getConstructor().newInstance();
                Method setMethod = object.getClass().getMethod("set" + StringUtil.initcap(cascade[x]), field.getType());
                setMethod.invoke(object, obj);  // 实现setter的调用
                // 修改当前的object类型
                object = obj;
            } else {
                object = instance;
            }
        }
        return object;
    }

    /**
     * 根据属性的类型将字符串进行数据类型转换
     * @param value 包含有数据内容的字符串
     * @param field 属性对应的类型
     * @return 返回转换后的具体数据类型
     */
    public static Object convert(String value, Field field) {
        String fieldTypeName = field.getType().getName();   // 获取当前的属性类型名称
        try {
            switch (fieldTypeName) {    // 通过switch进行处理
                case "java.lang.String":
                    return value;
                case "int":
                case "java.lang.Integer":
                    return Integer.parseInt(value);
                case "double":
                case "java.lang.Double":
                    return Double.parseDouble(value);
                case "long":
                case "java.lang.Long":
                    return Long.parseLong(value);
                case "java.lang.BigInteger":
                    return new BigInteger(value);
                case "java.math.BigDecimal":
                    return new BigDecimal(value);
                case "java.util.Date":
                    LocalDateTime localDateTime = LocalDateTime.parse(value, DATE_TIME_FORMATTER);
                    Instant instant = localDateTime.atZone(ZONE_ID).toInstant();
                    return Date.from(instant);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
